package cn.chengpengper.rpc.consumer;

import cn.chengpengper.rpc.common.exception.RegistryException;
import cn.chengpengper.rpc.consumer.common.RpcConsumer;
import cn.chengpengper.rpc.proxy.api.ProxyFactory;
import cn.chengpengper.rpc.proxy.api.async.IAsyncObjectProxy;
import cn.chengpengper.rpc.proxy.api.config.ProxyConfig;
import cn.chengpengper.rpc.proxy.api.object.ObjectProxy;
import cn.chengpengper.rpc.proxy.jdk.JdkProxyFactory;
import cn.chengpengper.rpc.registry.api.RegistryService;
import cn.chengpengper.rpc.registry.api.config.RegistryConfig;
import cn.chengpengper.rpc.registry.zookeeper.ZookeeperRegistryServiceImpl;
import cn.chengpengper.rpc.spi.loader.ExtensionLoader;
import lombok.extern.slf4j.Slf4j;
import org.apache.commons.lang3.StringUtils;

/**
 * @author chengpeng.hu
 */
@Slf4j
public class RpcClient {

    private String serviceVersion;

    private String serviceGroup;

    private String serializationType;

    private long timeout;

    private boolean async;

    private boolean oneway;

    private RegistryService registryService;

    private String proxy;

    //心跳间隔时间，默认30秒
    private int heartbeatInterval;

    //扫描空闲连接时间，默认60秒
    private int scanNotActiveChannelInterval;

    //重试间隔时间
    private int retryInterval = 1000;
    //重试次数
    private int retryTimes = 3;

    private boolean enableResultCache;

    private int resultCacheExpire;

    private boolean enableDirectServer;

    private String directServerUrl;

    //是否开启延迟连接
    private boolean enableDelayConnection = false;

    public RpcClient(String registryAddress,
                     String registryType,
                     String registryLoadBalanceType,
                     String proxy,
                     String serviceVersion,
                     String serviceGroup,
                     String serializationType,
                     long timeout,
                     boolean async,
                     boolean oneway,
                     int heartbeatInterval,
                     int scanNotActiveChannelInterval,
                     int retryInterval,
                     int retryTimes,
                     boolean enableResultCache,
                     int resultCacheExpire,
                     boolean enableDirectServer,
                     String directServerUrl,
                     boolean enableDelayConnection) {
        this.serviceVersion = serviceVersion;
        this.timeout = timeout;
        this.proxy = proxy;
        this.serviceGroup = serviceGroup;
        this.serializationType = serializationType;
        this.async = async;
        this.oneway = oneway;
        this.registryService = this.getRegistryService(registryAddress, registryType, registryLoadBalanceType);
        this.heartbeatInterval = heartbeatInterval;
        this.scanNotActiveChannelInterval = scanNotActiveChannelInterval;
        this.retryInterval = retryInterval;
        this.retryTimes = retryTimes;
        this.enableResultCache = enableResultCache;
        this.resultCacheExpire = resultCacheExpire;
        this.enableDirectServer = enableDirectServer;
        this.directServerUrl = directServerUrl;
        this.enableDelayConnection = enableDelayConnection;
    }

    private RegistryService getRegistryService(String registryAddress, String registryType, String registryLoadBalanceType) {
        if (StringUtils.isBlank(registryType)) {
            throw new IllegalArgumentException("registry type is null");
        }

        RegistryService registryService = ExtensionLoader.getExtension(RegistryService.class, registryType);
        try {
            registryService.init(new RegistryConfig(registryAddress, registryType, registryLoadBalanceType));
        } catch (Exception e) {
            log.error("RpcClient init registry service throws exception:{}", e);
            throw new RegistryException(e.getMessage(), e);
        }
        return registryService;
    }

    public <T> T create(Class<T> interfaceClass) {
        ProxyFactory proxyFactory = ExtensionLoader.getExtension(ProxyFactory.class, proxy);
        proxyFactory.init(
                new ProxyConfig(interfaceClass, serviceVersion, serviceGroup, serializationType, timeout, registryService,
                        RpcConsumer.getInstance()
                                .setHeartbeatInterval(heartbeatInterval)
                                .setRetryInterval(retryInterval)
                                .setDirectServerUrl(directServerUrl)
                                .setEnableDirectServer(enableDirectServer)
                                .setRetryTimes(retryTimes)
                                .setScanNotActiveChannelInterval(scanNotActiveChannelInterval)
                                .buildConnection(registryService)
                        , async, oneway, enableResultCache, resultCacheExpire
                ));
        return proxyFactory.getProxy(interfaceClass);
    }

    public <T> IAsyncObjectProxy createAsync(Class<T> interfaceClass) {
        return new ObjectProxy<T>(interfaceClass, serviceVersion, serviceGroup, serializationType, timeout, registryService,
                RpcConsumer.getInstance()
                        .setHeartbeatInterval(heartbeatInterval)
                        .setRetryInterval(retryInterval)
                        .setDirectServerUrl(directServerUrl)
                        .setEnableDirectServer(enableDirectServer)
                        .setRetryTimes(retryTimes)
                        .setScanNotActiveChannelInterval(scanNotActiveChannelInterval)
                        .buildConnection(registryService)
                , async, oneway, enableResultCache, resultCacheExpire);
    }

    public void shutdown() {
        RpcConsumer.getInstance().close();
    }
}
